/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdint>
#include <string>
#include <cstdio>

#include <ace/xcomponent/native_interface_xcomponent.h>
#include <arkui/native_node.h>
#include <arkui/native_interface.h>
#include <hilog/log.h>

#include "manager.h"


namespace NativeXComponentSample {
Manager Manager::manager_;

    Manager::~Manager() {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "~Manager");
        for (auto iter = nativeXComponentMap_.begin(); iter != nativeXComponentMap_.end(); ++iter) {
            if (iter->second != nullptr) {
//                delete iter->second;
                iter->second = nullptr;
            }
        }
        nativeXComponentMap_.clear();
    
        for (auto iter = containerMap_.begin(); iter != containerMap_.end(); ++iter) {
            if (iter->second != nullptr) {
                delete iter->second;
                iter->second = nullptr;
            }
        }
        containerMap_.clear();
    }

    napi_value Manager::CreateNativeNode(napi_env env, napi_callback_info info) {
        if ((env == nullptr) || (info == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode env or info is null");
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  1111");

        size_t argCnt = 1;
        napi_value args[1] = {nullptr};
        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode napi_get_cb_info failed");
        }

        if (argCnt != 1) {
            napi_throw_type_error(env, NULL, "Wrong number of arguments");
            return nullptr;
        }

        napi_valuetype valuetype;
        if (napi_typeof(env, args[0], &valuetype) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_typeof failed");
            return nullptr;
        }

        if (valuetype != napi_string) {
            napi_throw_type_error(env, NULL, "Wrong type of arguments");
            return nullptr;
        }

        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
        constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
        size_t length;
        if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_get_value_int64 failed");
            return nullptr;
        }
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  2222 %{public}s", idStr);

        auto manager = Manager::GetInstance();
        if (manager == nullptr) {
            return nullptr;
        }

        OH_NativeXComponent *component = manager->GetNativeXComponent(idStr);
        if (component == nullptr) {
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  3333333");
        auto nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1*>(OH_ArkUI_GetNativeAPI(ARKUI_NATIVE_NODE, 1));
        if (nodeAPI != nullptr) {
            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  34444");
            if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
                OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "OnSurfaceCreatedCB  222--1");
                ArkUI_NumberValue widthValue[] = {300};
                ArkUI_AttributeItem widthItem = { widthValue, 1 };
                ArkUI_NumberValue heightValue1[] = {500};
                ArkUI_AttributeItem heightItem1 = { heightValue1, 1 };
                ArkUI_NumberValue colorValue[] = {{.u32=0xFFFF0000}};
                ArkUI_AttributeItem colorItem = { colorValue, 1 };
                auto stack = nodeAPI->createNode(ARKUI_NODE_STACK);
                auto text = nodeAPI->createNode(ARKUI_NODE_TEXT);
                auto toggle = nodeAPI->createNode(ARKUI_NODE_TOGGLE);
                ArkUI_AttributeItem contenItem = { .string="liyi" };
                ArkUI_NumberValue sizeValue[] = {40};
                ArkUI_AttributeItem sizeItem = { sizeValue, 1 };
                ArkUI_NumberValue sizecolor[] = {{.u32=0xFF0000FF}};
                ArkUI_AttributeItem contentColor = { sizecolor, 1 };
                ArkUI_NumberValue shadow[] = { 10, {.i32=ARKUI_SHADOW_TYPE_COLOR}, {.u32=0xFF00FF00}, 10, 10 };
                ArkUI_AttributeItem shadowitem = { shadow, sizeof(shadow)/sizeof(ArkUI_NumberValue) };
                ArkUI_NumberValue togglecolor[] = {{.u32=0xFF00FF00}};
                ArkUI_AttributeItem toggleColor = { togglecolor, 1 };
                ArkUI_NumberValue repeat[] = {{.i32=ARKUI_IMAGE_REPEAT_X}};
                ArkUI_AttributeItem repeatItem = { repeat, 1, "/pages/common/icon.png" };
                ArkUI_NumberValue weight[] = {{.i32=ARKUI_FONT_WEIGHT_BOLD}};
                ArkUI_AttributeItem weightItem = { weight, 1 };
                ArkUI_NumberValue copy[] = {{.i32=ARKUI_TEXT_COPY_OPTIONS_LOCAL_DEVICE}};
                ArkUI_AttributeItem copyItem = { copy, 1 };
                ArkUI_NumberValue offset[] = {100};
                ArkUI_AttributeItem offsetItem = { offset, 1 };
                nodeAPI->setAttribute(text, NODE_TEXT_CONTENT, &contenItem);
                nodeAPI->setAttribute(text, NODE_FONT_SIZE, &sizeItem);
                nodeAPI->setAttribute(text, NODE_FONT_COLOR, &contentColor);
                nodeAPI->setAttribute(text, NODE_FONT_WEIGHT, &weightItem);
                nodeAPI->setAttribute(text, NODE_TEXT_TEXT_SHADOW, &shadowitem);
                nodeAPI->setAttribute(text, NODE_TEXT_BASELINE_OFFSET, &offsetItem);
            nodeAPI->setAttribute(text, NODE_TEXT_COPY_OPTION, &copyItem);
                nodeAPI->setAttribute(stack, NODE_BACKGROUND_COLOR, &colorItem);
                nodeAPI->setAttribute(stack, NODE_WIDTH, &widthItem);
                nodeAPI->setAttribute(stack, NODE_HEIGHT, &heightItem1);
                nodeAPI->setAttribute(stack, NODE_BACKGROUND_IMAGE, &repeatItem);
                nodeAPI->setAttribute(toggle, NODE_TOGGLE_SELECTED_COLOR, &toggleColor);
                nodeAPI->setAttribute(toggle, NODE_TOGGLE_SWITCH_POINT_COLOR, &toggleColor);
                nodeAPI->addChild(stack, text);
//                nodeAPI->addChild(stack, toggle);
                OH_NativeXComponent_AttachNativeRootNode(component, stack);
                nodeAPI->resetAttribute(text, NODE_FONT_COLOR);
            }
        }
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode  555");
        return nullptr;
    }


    napi_value Manager::UpdateNativeNode(napi_env env, napi_callback_info info) {

        if ((env == nullptr) || (info == nullptr )) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "UpdateNativeNode env or info is null");
            return nullptr;
        }

        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  1111");

        size_t argCnt = 1;
        napi_value args[1] = {nullptr};
        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "UpdateNativeNode napi_get_cb_info failed");
        }

        if (argCnt != 1) {
            napi_throw_type_error(env, NULL, "Wrong number of arguments");
            return nullptr;
        }

        napi_valuetype valuetype;
        if (napi_typeof(env, args[0], &valuetype) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_typeof failed");
            return nullptr;
        }

        if (valuetype != napi_string) {
            napi_throw_type_error(env, NULL, "Wrong type of arguments");
            return nullptr;
        }

        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
        constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
        size_t length;
        if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_get_value_int64 failed");
            return nullptr;
        }
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "CreateNativeNode  1111 %{public}s", idStr);

        auto manager = Manager::GetInstance();
        if (manager == nullptr) {
            return nullptr;
        }

        OH_NativeXComponent *component = manager->GetNativeXComponent(idStr);
        if (component == nullptr) {
            return nullptr;
        }


        if ((env == nullptr) || (info == nullptr || component == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext env or info is null");
            return nullptr;
        }
    
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  1111");
        auto nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1*>(OH_ArkUI_GetNativeAPI(ARKUI_NATIVE_NODE, 1));
        if (nodeAPI != nullptr) {
            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  222");
    
            if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
                OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  222--1");
    
//                const ArkUICommonModifierAPI *modifier = nodeAPI->GetCommonModifier();
//    
//                ArkUI_NodeHandle text1 = nodeAPI->GetNodeById(100);
//                ArkUI_NodeHandle text2 = nodeAPI->GetNodeById(102);
//                ArkUI_NodeHandle text3 = nodeAPI->GetNodeById(104);
//                ArkUI_NodeHandle stack = nodeAPI->GetNodeById(105);
//            
//                modifier->SetWidth(text2, 0.7, 3, nullptr);   // 100 percent
//                modifier->SetHeight(text2, 55, 0, nullptr); // px
//                modifier->SetBackgroundColor(text2, 0xff00ff00);
//            
//                nodeAPI->RemoveChild(stack, text1);
//                nodeAPI->RemoveChild(stack, text3);
//                OH_NativeXComponent_MarkDirtyContainer(component, 1 << 9);
            }
        }
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode  333");
        return nullptr;
    }


    napi_value Manager::GetContext(napi_env env, napi_callback_info info) {
        if ((env == nullptr) || (info == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext env or info is null");
            return nullptr;
        }

        size_t argCnt = 1;
        napi_value args[1] = {nullptr};
        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext napi_get_cb_info failed");
        }

        if (argCnt != 1) {
            napi_throw_type_error(env, NULL, "Wrong number of arguments");
            return nullptr;
        }

        napi_valuetype valuetype;
        if (napi_typeof(env, args[0], &valuetype) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_typeof failed");
            return nullptr;
        }

        if (valuetype != napi_number) {
            napi_throw_type_error(env, NULL, "Wrong type of arguments");
            return nullptr;
        }

        int64_t value;
        if (napi_get_value_int64(env, args[0], &value) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_get_value_int64 failed");
            return nullptr;
        }

        napi_value exports;
        if (napi_create_object(env, &exports) != napi_ok) {
            napi_throw_type_error(env, NULL, "napi_create_object failed");
            return nullptr;
        }

        return exports;
    }

    void Manager::Export(napi_env env, napi_value exports) {
        if ((env == nullptr) || (exports == nullptr)) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: env or exports is null");
            return;
        }

        napi_value exportInstance = nullptr;
        if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: napi_get_named_property fail");
            return;
        }

        OH_NativeXComponent *nativeXComponent = nullptr;
        if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: napi_unwrap fail");
            return;
        }
   
        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
        if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) !=
            OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager",
                         "Export: OH_NativeXComponent_GetXComponentId fail");
            return;
        }

        std::string id(idStr);
        auto manager = Manager::GetInstance();
        if ((manager != nullptr) && (nativeXComponent != nullptr)) {
            manager->SetNativeXComponent(id, nativeXComponent);
            auto container = manager->GetContainer(id);
            if (container != nullptr) {
                container->RegisterCallback(nativeXComponent);
                //            container->Export(env, exports);
            }
        }
    }

    void Manager::SetNativeXComponent(std::string & id, OH_NativeXComponent * nativeXComponent) {
        if (nativeXComponent == nullptr) {
            return;
        }

        if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) {
            nativeXComponentMap_[id] = nativeXComponent;
            return;
        }

        if (nativeXComponentMap_[id] != nativeXComponent) {
            OH_NativeXComponent *tmp = nativeXComponentMap_[id];
            delete tmp;
            tmp = nullptr;
            nativeXComponentMap_[id] = nativeXComponent;
        }
    }

    OH_NativeXComponent* Manager::GetNativeXComponent(const std::string &id) {
        return nativeXComponentMap_[id];
    }

    Container *Manager::GetContainer(std::string & id) {
        if (containerMap_.find(id) == containerMap_.end()) {
            Container *instance = Container::GetInstance(id);
            containerMap_[id] = instance;
            return instance;
        }

        return containerMap_[id];
    }
} // namespace NativeXComponentSample
